﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Common.Encryption
{
    /// <summary>
    /// Author: Mike Barksdale
    /// Generic interface for basic encryption.
    /// </summary>
    public interface IEncryptionProvider
    {
        string Encrypt(string stringToEncrypt, string key);
        string Decrypt(string stringToDecrypt, string key);
        void SetPrivateKey(string key);
        string GetPrivateKey();
        void SetSalt(string salt);
        string GetSalt();
    }

    /// <summary>
    /// Author: Mike Barksdale
    /// Class that implements the IEncryptionProvider interface. Uses AES encryption.
    /// </summary>
    public class AesEncryptionProvider : IEncryptionProvider
    {
        private string _key = "th1$A8ntCr@ckEd";
        private byte[] _salt = Encoding.ASCII.GetBytes("NAKSdoA27yaZ");

        /// <summary>
        /// Author: Mike Barksdale
        /// Encrypts a string using the key provided.
        /// </summary>
        /// <param name="stringToEncrypt">The string to encrypt</param>
        /// <param name="key">The key to use for encryption</param>
        /// <returns>The encrypted string</returns>
        public string Encrypt(string stringToEncrypt, string key)
        {
            if (string.IsNullOrEmpty(stringToEncrypt))
                throw new ArgumentNullException("stringToEncrypt");

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");

            AesManaged aes = new AesManaged();

            string encryptedString = null;

            var generatedKey = new Rfc2898DeriveBytes(key, _salt);

            aes.Key = generatedKey.GetBytes(aes.KeySize / 8);
            aes.IV = generatedKey.GetBytes(aes.BlockSize / 8);

            var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    using (var sw = new StreamWriter(cs))
                    {
                        sw.Write(stringToEncrypt);
                    }
                }

                encryptedString = Convert.ToBase64String(ms.ToArray());
            }

            aes.Clear();

            return encryptedString;
        }

        /// <summary>
        /// Author: Mike Barksdale
        /// Decrypts an encrypted string using the given key.
        /// </summary>
        /// <param name="stringToDecrypt">The encrypted string to decrypt</param>
        /// <param name="key">The key for decryption</param>
        /// <returns>The decrypted string</returns>
        public string Decrypt(string stringToDecrypt, string key)
        {
            if (string.IsNullOrEmpty(stringToDecrypt))
                throw new ArgumentNullException("stringToDecrypt");

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");

            var aes = new AesManaged();

            string decryptedString = null;

            var generatedKey = new Rfc2898DeriveBytes(key, _salt);

            aes.Key = generatedKey.GetBytes(aes.KeySize / 8);
            aes.IV = generatedKey.GetBytes(aes.BlockSize / 8);

            var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            var bytes = Convert.FromBase64String(stringToDecrypt);

            using (var ms = new MemoryStream(bytes))
            {
                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                {
                    using (var sr = new StreamReader(cs))
                    {
                        decryptedString = sr.ReadToEnd();
                    }
                }
            }

            aes.Clear();

            return decryptedString;
        }

        /// <summary>
        /// Author: Mike Barksdale
        /// Getter method for returning the private key used by the class.
        /// </summary>
        /// <returns>The private key set on the class</returns>
        public string GetPrivateKey()
        {
            return _key;
        }

        /// <summary>
        /// Author: Mike Barksdale
        /// Setter method for setting the private key for use by the class.
        /// </summary>
        /// <param name="key">String representation of the private key</param>
        public void SetPrivateKey(string key)
        {
            this._key = key;
        }

        /// <summary>
        /// Author: Mike Barksdale
        /// Getter method for returning the salt used by the class.
        /// </summary>
        /// <returns>The private salt set on the class</returns>
        public string GetSalt()
        {
            return Encoding.ASCII.GetString(this._salt);
        }

        /// <summary>
        /// Author: Mike Barksdale
        /// Setter method for setting the private salt for use by the class.
        /// </summary>
        /// <param name="salt">String representation of the salt</param>
        public void SetSalt(string salt)
        {
            this._salt = Encoding.ASCII.GetBytes(salt);
        }
    }
}
